package com.ben.chatroom.server;

import com.ben.chatroom.common.cocurrent.FutureTaskScheduler;
import com.ben.chatroom.common.codec.ProtobufDecoder;
import com.ben.chatroom.common.codec.ProtobufEncoder;
import com.ben.chatroom.distribute.ImWoker;
import com.ben.chatroom.distribute.WorkerRouter;
import com.ben.chatroom.handler.ChatRedirectHandler;
import com.ben.chatroom.handler.LoginRequestHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.InetSocketAddress;

/**
 * @author huangyijun
 */
@Slf4j
@Service
public class ChatServer {

  @Value("${chat.host}")
  private String host;

  @Value("${chat.port}")
  private int port;

  private EventLoopGroup bossGroup;
  private EventLoopGroup workerGroup;

  private final LoginRequestHandler loginRequestHandler;
  private final ChatRedirectHandler chatRedirectHandler;
  private final ServerBootstrap bootstrap = new ServerBootstrap();

  public ChatServer(LoginRequestHandler loginRequestHandler, ChatRedirectHandler chatRedirectHandler) {
    this.loginRequestHandler = loginRequestHandler;
    this.chatRedirectHandler = chatRedirectHandler;
  }

  public void run() {
    bossGroup = new NioEventLoopGroup();
    workerGroup = new NioEventLoopGroup();

    bootstrap.group(bossGroup, workerGroup);
    bootstrap.channel(NioServerSocketChannel.class);
    bootstrap.localAddress(new InetSocketAddress(host, port));

    bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
    bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

    bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
      @Override
      protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new ProtobufDecoder());
        pipeline.addLast(new ProtobufEncoder());
        pipeline.addLast("loginRequestHandler", loginRequestHandler);
        pipeline.addLast("chatRedirect", chatRedirectHandler);

      }
    });

    ChannelFuture channelFuture = null;
    boolean isStart = false;

    while(!isStart) {

      try {
        channelFuture = bootstrap.bind().sync();
        isStart = true;
        log.info("聊天室框架启动成功,启动端口: " + channelFuture.channel().localAddress());
      }
      catch(Exception e) {
        log.error("启动失败，失败原因:" + e.getMessage());
        port++;
        log.info("尝试新的端口:" + port);
        bootstrap.localAddress(new InetSocketAddress(port));
      }
    }

    ImWoker.getInst().setLocalNode(host, port);

    FutureTaskScheduler.add(() -> {
      /**
       * 启动节点
       */
      ImWoker.getInst().init();
      /**
       * 启动节点的管理
       */
      WorkerRouter.getInst().init();

    });

    //JVM关闭时的钩子函数
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
    }));

    try {
      channelFuture.channel().closeFuture().sync();
    }
    catch(Exception e) {
      log.error(e.getMessage());
    }
    finally {
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
    }

  }

}
